/**
 * @file port_gpio.c
 * @brief
 * @author lijingjie (lijingjie@pthyidh.com)
 * @date 2024-11-01
 *
 * @copyright Copyright (c) 2024 by 深圳市鹏天微智能科技有限公司, All Rights Reserved.
 */

/* ==================== [Includes] ========================================== */

#include "xf_hal_port.h"

#if XF_HAL_GPIO_IS_ENABLE

#include <stdio.h>
#include <stdlib.h>

#include "pt3220.h"
#include "drvs.h"

#include "xf_utils.h"
#include "xf_task.h"
#include "xf_init.h"
#include "xf_heap.h"

/* ==================== [Defines] =========================================== */

#define XF_HAL_GPIO_DEFAULT_DIRECTION        XF_HAL_GPIO_DIR_IN
#define XF_HAL_GPIO_DEFAULT_SPEED            0
#define XF_HAL_GPIO_DEFAULT_PULL             XF_HAL_GPIO_PULL_NONE
#define XF_HAL_GPIO_DEFAULT_INTR_ENABLE      0
#define XF_HAL_GPIO_DEFAULT_INTR_TYPE        XF_HAL_GPIO_INTR_TYPE_DISABLE
#define XF_HAL_GPIO_DEFAULT_CB_CALLBACK      NULL
#define XF_HAL_GPIO_DEFAULT_CB_USER_DATA     NULL
#define XF_HAL_GPIO_DEFAULT_ISR_CALLBACK     NULL
#define XF_HAL_GPIO_DEFAULT_ISR_USER_DATA    NULL

#define TAG "PORT_GPIO"

#define PT_GPIO_MAX                     PA_MAX

#define PORT_GPIO_TOPIC_ID  (uint32_t)(-1)

/* ==================== [Typedefs] ========================================== */

typedef struct isr_gpio {
    xf_list_t node;
    xf_hal_dev_t *dev;
} isr_gpio_t;

/* ==================== [Static Prototypes] ================================= */

// 用户实现对接的部分
static int port_gpio_open(xf_hal_dev_t *dev);
static int port_gpio_ioctl(xf_hal_dev_t *dev, uint32_t cmd, void *config);
static int port_gpio_read(xf_hal_dev_t *dev, void *buf, size_t count);
static int port_gpio_write(xf_hal_dev_t *dev, const void *buf, size_t count);
static int port_gpio_close(xf_hal_dev_t *dev);

static void xf_isr_task_sub(const void *const data, void *user_data);

/* ==================== [Static Variables] ================================== */

// 中断引脚表，用于记录中断发生时，运行哪一个中断回调函数
static xf_list_t _isr_gpio_list = XF_LIST_HEAD_INIT(_isr_gpio_list);

/* ==================== [Macros] ============================================ */

/* ==================== [Global Functions] ================================== */

// 中断接口
void EXTI_IRQHandler(void)
{
    uint32_t irq_sta = exti_int_get_all(); // 取中断寄存器所有位

    isr_gpio_t *isr_gpio_config = NULL;
    xf_list_for_each_entry(isr_gpio_config, &_isr_gpio_list, isr_gpio_t, node) {
        if (irq_sta & EXTI_SRC(isr_gpio_config->dev->id)) {
            xf_hal_dev_t *dev = (xf_hal_dev_t *)isr_gpio_config->dev;
            xf_hal_gpio_callback_t *cb = (xf_hal_gpio_callback_t *)dev->platform_data;
            if (cb->callback) {
                cb->callback(dev->id, gpio_get(dev->id), cb->user_data);
            }
            xf_task_mbus_pub_async(PORT_GPIO_TOPIC_ID, (void *)isr_gpio_config->dev);
            exti_int_clr(isr_gpio_config->dev->id);
        }
    }
}

/* ==================== [Static Functions] ================================== */

static void xf_isr_task_sub(const void *const data, void *user_data)
{
    xf_hal_dev_t *dev = (xf_hal_dev_t *)data;
    xf_hal_gpio_callback_t *cb = (xf_hal_gpio_callback_t *)user_data;
    if (cb->callback) {
        cb->callback(dev->id, gpio_get(dev->id), cb->user_data);
    }
}

// 需要对接的操作函数，上层应用通过下面的接口操作硬件
static int port_gpio_open(xf_hal_dev_t *dev)
{

    if (dev->id > PT_GPIO_MAX) {
        XF_LOGE(TAG, "gpio number is error");
        return XF_ERR_INVALID_ARG;
    }

    return XF_OK;
}

static int port_gpio_ioctl(xf_hal_dev_t *dev, uint32_t cmd, void *config)
{
    xf_hal_gpio_config_t *gpio_config = (xf_hal_gpio_config_t *)config;

    if (cmd == XF_HAL_GPIO_CMD_DEFAULT) {
        gpio_config->direction        = XF_HAL_GPIO_DEFAULT_DIRECTION;
        gpio_config->speed            = XF_HAL_GPIO_DEFAULT_SPEED;
        gpio_config->pull             = XF_HAL_GPIO_DEFAULT_PULL;
        gpio_config->intr_enable      = XF_HAL_GPIO_DEFAULT_INTR_ENABLE;
        gpio_config->intr_type        = XF_HAL_GPIO_DEFAULT_INTR_TYPE;
        gpio_config->cb.callback      = XF_HAL_GPIO_DEFAULT_CB_CALLBACK;
        gpio_config->cb.user_data     = XF_HAL_GPIO_DEFAULT_CB_USER_DATA;
        gpio_config->isr.callback     = XF_HAL_GPIO_DEFAULT_ISR_CALLBACK;
        gpio_config->isr.user_data    = XF_HAL_GPIO_DEFAULT_ISR_USER_DATA;
        dev->platform_data            = &gpio_config->isr;
        XF_LOGI(TAG, "gpio set default");
        return XF_OK;
    }

    if (cmd == XF_HAL_GPIO_CMD_ALL) {
        return XF_OK;
    }

    if (cmd & XF_HAL_GPIO_CMD_DIRECTION || cmd & XF_HAL_GPIO_CMD_PULL) {
        uint8_t direction = gpio_config->direction;
        uint8_t pull = gpio_config->pull;
        uint32_t gpio_pin = dev->id;
        if (direction == XF_HAL_GPIO_DIR_IN) { // 输入
            if (pull == XF_HAL_GPIO_PULL_UP) { // 上拉输入
                gpio_dir_input(gpio_pin, IE_UP);
            } else if (pull == XF_HAL_GPIO_PULL_DOWN) { // 下拉输入
                gpio_dir_input(gpio_pin, IE_DOWN);
            } else { // 高阻态
                gpio_dir_input(gpio_pin, IE_AIR);
            }
        } else if (direction == XF_HAL_GPIO_DIR_OUT) { // 输出
            if (pull == XF_HAL_GPIO_PULL_UP) { // 上拉输入
                gpio_dir_output(gpio_pin, OE_HIGH);
            } else {
                gpio_dir_output(gpio_pin, OE_LOW);
            }
        } else if (direction == XF_HAL_GPIO_DIR_OUT_OD) { // 开漏
            return XF_ERR_NOT_SUPPORTED;
        }
    }

    if (cmd & XF_HAL_GPIO_CMD_SPEED) { // 设置引脚速度
        return XF_ERR_NOT_SUPPORTED;
    }

    if (cmd & XF_HAL_GPIO_CMD_INTR_ENABLE) { // 中断使能
        if (gpio_config->intr_enable) {
            xf_task_mbus_sub(PORT_GPIO_TOPIC_ID, xf_isr_task_sub, &gpio_config->cb);
            NVIC_EnableIRQ(EXTI_IRQn); // 外部中断，可以开启多个，
        } else {
            xf_task_mbus_unsub(PORT_GPIO_TOPIC_ID, xf_isr_task_sub);
            // NVIC_DisableIRQ(EXTI_IRQn);
        }
    }

    if (cmd & XF_HAL_GPIO_CMD_INTR_TYPE) { // 设置中断类型
        static char flag = 1;
        uint32_t isr_type = gpio_config->intr_type;
        uint32_t gpio_pin = dev->id;
        if (flag) {
            exti_init(EXTI_DBC(15, 4)); // 需要先初始化，才会生效
            flag = 0;
        }

        if (isr_type == XF_HAL_GPIO_INTR_TYPE_RISING) {
            exti_set(EXTI_RTS, exti_get(EXTI_RTS) | (EXTI_SRC(gpio_pin))); // rising
        } else if (isr_type == XF_HAL_GPIO_INTR_TYPE_FALLING) {
            exti_set(EXTI_FTS, exti_get(EXTI_FTS) | (EXTI_SRC(gpio_pin))); // falling
        } else if (XF_HAL_GPIO_INTR_TYPE_ANY == isr_type) {
            exti_set(EXTI_FTS, exti_get(EXTI_FTS) | (EXTI_SRC(gpio_pin))); // falling
            exti_set(EXTI_RTS, exti_get(EXTI_RTS) | (EXTI_SRC(gpio_pin)));
        }
        exti_set(EXTI_DBE, exti_get(EXTI_DBE) | (EXTI_SRC(gpio_pin)));
        exti_set(EXTI_IER, exti_get(EXTI_IER) | (EXTI_SRC(gpio_pin)));
    }

    if (cmd & XF_HAL_GPIO_CMD_INTR_CB) { // 任务触发中断回调函数在这里注册
        isr_gpio_t *isr_gpio_config = NULL;

        xf_list_for_each_entry(isr_gpio_config, &_isr_gpio_list, isr_gpio_t, node) {
            if (isr_gpio_config->dev->id == dev->id) {
                return XF_OK;
            }
        }

        // GPIO 未注册中断回调函数
        isr_gpio_config = (isr_gpio_t *)xf_malloc(sizeof(isr_gpio_t));

        if (isr_gpio_config == NULL) {
            XF_LOGE(TAG, "memory alloc failed!");
            return XF_ERR_NO_MEM;
        }

        xf_list_init(&isr_gpio_config->node);

        isr_gpio_config->dev = dev;

        xf_list_add_tail(&isr_gpio_config->node, &_isr_gpio_list);
    }

    if (cmd & XF_HAL_GPIO_CMD_INTR_ISR) { // 硬件中断回调函数应该从这里注册
        isr_gpio_t *isr_gpio_config = NULL;

        xf_list_for_each_entry(isr_gpio_config, &_isr_gpio_list, isr_gpio_t, node) {
            if (isr_gpio_config->dev->id == dev->id) {
                return XF_OK;
            }
        }

        // GPIO 未注册中断回调函数
        isr_gpio_config = (isr_gpio_t *)xf_malloc(sizeof(isr_gpio_t));

        if (isr_gpio_config == NULL) {
            XF_LOGE(TAG, "memory alloc failed!");
            return XF_ERR_NO_MEM;
        }

        xf_list_init(&isr_gpio_config->node);

        isr_gpio_config->dev = dev;

        xf_list_add_tail(&isr_gpio_config->node, &_isr_gpio_list);
    }

    return XF_OK;
}

static int port_gpio_read(xf_hal_dev_t *dev, void *buf, size_t count)
{
    bool *level = (bool *)buf;
    *level = gpio_get(dev->id);

    return 1;
}

static int port_gpio_write(xf_hal_dev_t *dev, const void *buf, size_t count)
{
    bool level = *(bool *)buf;
    gpio_put(dev->id, level);

    return 1;
}

static int port_gpio_close(xf_hal_dev_t *dev)
{
    gpio_set_hiz(dev->id);
    isr_gpio_t *isr_gpio_config = NULL, *_tmp;

    xf_list_for_each_entry_safe(isr_gpio_config, _tmp, &_isr_gpio_list, isr_gpio_t, node) {
        if (isr_gpio_config->dev->id == dev->id) {
            xf_list_del(&isr_gpio_config->node);
            xf_free(isr_gpio_config);
            return XF_OK;
        }
    }

    return XF_OK;
}

static int xf_hal_gpio_reg(void)
{
    xf_driver_ops_t ops = {
        .open = port_gpio_open,
        .ioctl = port_gpio_ioctl,
        .write = port_gpio_write,
        .read = port_gpio_read,
        .close = port_gpio_close,
    };
    xf_hal_gpio_register(&ops);
    xf_task_mbus_reg_topic(PORT_GPIO_TOPIC_ID, sizeof(xf_hal_dev_t));
    return XF_OK;
}

XF_INIT_EXPORT_PREV(xf_hal_gpio_reg);

#endif
